#ifndef _FORWARDERPROPHET_H
#define _FORWARDERPROPHET_H

class ForwarderProphet;

#include "ForwarderAsynchronous.h"
#include <libcpphaggle/Map.h>
#include <libcpphaggle/String.h>

using namespace haggle;

#define LABEL_T string
#define RANK_T long

typedef unsigned long bubble_node_id_t;

#define this_node_id ((bubble_node_id_t) 1)

typedef Pair<LABEL_T, RANK_T> bubble_metric_t;

typedef Map<bubble_node_id_t, bubble_metric_t> bubble_rib_t;

#define NF_max (5) // Not sure what a good number is here...

#define HOSTNAME_LEN 64

/**
 * Forwarding module
 */

class ForwarderProphet : public ForwarderAsynchronous {

#define PROPHET_NAME "Prophet"

private:  

	LABEL_T myLabel;
	RANK_T myRank;

	bubble_node_id_t myNodeId;
	string myNodeStr;
	
	char hostname[HOSTNAME_LEN];
	
	HaggleKernel *kernel;

	/**
	 * convert table 
	 */   
	Map<string, bubble_node_id_t> nodeid_to_id_number;
	Map<bubble_node_id_t, string> id_number_to_nodeid;

	/**
		The next id number to use. Since the default value for a 
		bubble_node_id_t in a map is 0, this should never be 0, in order 
		to avoid confusion.
	*/
	bubble_node_id_t next_id_number;
	
	/**
		This is the local node's internal PRoPHET metrics.
	*/
	bubble_rib_t rib;
	Timeval rib_timestamp;
	
	size_t getSaveState(RepositoryEntryList& rel);
	bool setSaveState(RepositoryEntryRef& e);
	
	/**
	 This function returns the nonzero id number for the given node id 
	 string. It ensures that the node id is in the nodeid_to_id_number map
	 and that the returned id number is nonzero. If the node id wasn't in the
	 map to begin with, it is inserted, along with a new id number.
	 */
	bubble_node_id_t id_from_string(const string& nodeid);
	
	bubble_metric_t& age_metric(bubble_metric_t& metric, bool force = false);
	
	bool newRoutingInformation(const Metadata *m);
	
	bool addRoutingInformation(DataObjectRef& dObj, Metadata *parent);

	/**
		Does the actual work of newNeighbor.
	*/
	void _newNeighbor(const NodeRef &neighbor);

	/**
		Does the actual work of endNeighbor.
	*/
	void _endNeighbor(const NodeRef &neighbor);
	
	/**
		Does the actual work of getTargetsFor.
	*/
	void _generateTargetsFor(const NodeRef &neighbor);
	
	/**
		Does the actual work of getDelegatesFor.
	*/
	void _generateDelegatesFor(const DataObjectRef &dObj, const NodeRef &target, const NodeRefList *other_targets);
#ifdef DEBUG
	/**
		Does the actual work or printRoutingTable().
	*/
	void _printRoutingTable(void);
#endif
	void _onForwarderConfig(const Metadata& m);

public:
	ForwarderProphet(ForwardingManager *m = NULL, const EventType type = -1);
	~ForwarderProphet();
};
#endif
